---
title: Intro
description: A seriously tiny type-safe wrapper around existing, battle-tested, established tech.
---

Simple cross-stack type-safety for your API, with just a sprinkle of TypeScript magic ✨

- 🛟 Contract-First API
- 🌈 It's just HTTP/REST
- 🔒 Supports all Standard Schema validation libraries
- 📦 OpenAPI generation

### Define your API contract

Define an API contact, allowing you to share your API contract between client and server, or even publish it for a third party to consume.

We support any Typescript validation library that supports [Standard Schema](https://standardschema.dev), or if you want you can use plain TS types and get no runtime validation.

<Tabs items={['Zod', 'Valibot', 'Arktype', 'None']}>
  <Tab value="Zod">
```typescript title="contract.ts"
    import { initContract } from '@ts-rest/core';
    import { z } from 'zod'; // [!code highlight]

    const c = initContract();

    export const contract = c.router({
      getPokemon: {
        method: 'GET',
        path: '/pokemon/:id',
        responses: {
          200: z.object({ // [!code highlight]
            name: z.string(), // [!code highlight]
          }), // [!code highlight]
        },
      },
    });
    ```

  </Tab>
  <Tab value="Valibot">
    ```typescript title="contract.ts"
    import { initContract } from '@ts-rest/core';
    import * as v from 'valibot'; // [!code highlight]

    const c = initContract();

    export const contract = c.router({
      getPokemon: {
        method: 'GET',
        path: '/pokemon/:id',
        responses: {
          200: v.object({ // [!code highlight]
            name: v.string(), // [!code highlight]
          }), // [!code highlight]
        },
      },
    });
    ```

  </Tab>
  <Tab value="Arktype">
    ```typescript title="contract.ts"
    import { initContract } from '@ts-rest/core';
    import { type } from 'arktype'; // [!code highlight]

    const c = initContract();

    export const contract = c.router({
      getPokemon: {
        method: 'GET',
        path: '/pokemon/:id',
        responses: {
          200: type({ // [!code highlight]
            name: 'string', // [!code highlight]
          }), // [!code highlight]
        },
      },
    });
    ```

  </Tab>
   <Tab value="None">
    ```typescript title="contract.ts"
    import { initContract } from '@ts-rest/core';

    const c = initContract();

    export const contract = c.router({
      getPokemon: {
        method: 'GET',
        path: '/pokemon/:id',
        responses: {
          200: c.type<{ name: string }>(), // [!code highlight]
        },
      },
    });
    ```

  </Tab>
</Tabs>

### Fulfill the contract on your server

We support Nest, Next.js, Express, Fastify and more - Our server implementation allows you to guarentee your backend stays in sync with your API contract.

```typescript twoslash title="server.ts"
import { initContract } from '@ts-rest/core';
const c = initContract();

export const contract = c.router({
  getPokemon: {
    method: 'GET',
    path: '/pokemon/:id',
    responses: {
      200: c.type<{ name: string }>(),
    },
  },
});

const pokemonService = {
  getPokemon: async (id: string) => {
    return { name: 'pikachu' };
  },
};

// ---cut---
import { initServer } from '@ts-rest/express';

const s = initServer();

const router = s.router(contract, {
  getPokemon: async ({ params: { id } }) => {
    //                  ^?
    const pokemon = await pokemonService.getPokemon(id);

    return {
      status: 200,
      body: pokemon,
    };
  },
});
```

### Use the API on the client

Our `@ts-rest/core` package provides a super light-weight fetch based client - We remain close to HTTP fundamentals, so we always return a status code, and body.

```typescript twoslash title="client.ts"
import { initContract } from '@ts-rest/core';
const c = initContract();

export const contract = c.router({
  getPokemon: {
    method: 'GET',
    path: '/pokemon/:id',
    responses: {
      200: c.type<{ name: string }>(),
    },
  },
});

// ---cut---
import { initClient } from '@ts-rest/core';

const client = initClient(contract, {
  baseUrl: 'http://localhost:3000',
});

const result = await client.getPokemon({
  params: { id: '1' },
  // ^?
});

if (result.status === 200) {
  result.body;
  //      ^?
}
```

---

## Meet the team

We've got many, many contributors, but the core team consists of:

- [@oliverbutler](https://github.com/oliverbutler) - Founder of ts-rest, Lead Engineer at [Onsi](https://onsi.com)
- [@gabrola](https://github.com/Gabrola) - Typescript Expert and first to join the team
- [@michaelangeloio](https://github.com/michaelangeloio) - SWE at [GitLab](https://gitlab.com), second to join the team

We all work full time, so we tend to have non overlapping schedules, which normally means at some point one of us will be available to help with
issues - We respond more easily on GitHub than on Discord, although reach out to us on Discord if you'd like to chat.
